#!/usr/bin/env python
# -*- encoding: utf-8 -*-
'''
@File    :   init.py
@Time    :   2021/02/05 21:26:59
@Author  :   superjin
@Version :   1.0
'''
import traceback

from fastapi import FastAPI, Request, Response
from starlette.middleware.cors import CORSMiddleware
from setting.web import settings
from fastapi.exceptions import RequestValidationError, ValidationException
from exceptions import custom_exc
from core.response import response_code
from common.logger import logger
# from db.rediscli import redis_client
from router.v1_router import api_v1_router

tags_metadata = [
    {
        "name": "Auth",
        "description": "用户登录和注册",
    },
    {
        "name": "User",
        "description": "用户操作",
    },
    {
        "name": "Classroom",
        "description": "教室操作",
    },
    {
        "name": "Reservation",
        "description": "预约操作",
    },
    {
        "name": "Participant",
        "description": "参与者操作",
    },
    {
        "name": "System",
        "description": "系统操作",
    },
    {
        "name": "File",
        "description": "文件操作",
    },
]


def create_app() -> FastAPI:
    app = FastAPI(
        debug=settings.DEBUG,
        title=settings.TITLE,
        description=settings.DESCRIPTION,
        docs_url=settings.DOCS_URL,
        openapi_url=settings.OPENAPI_URL,
        redoc_url=settings.REDOC_URL,
        version=settings.VERSION,
        # See: https://fastapi.tiangolo.com/tutorial/metadata/#metadata-for-tags
        openapi_tags=tags_metadata,
    )

    # 允许跨域
    register_cors(app)

    # 注册路由
    register_router(app)

    register_exception(app)

    register_hook(app)
    # 注册事件
    register_init(app)

    
    return app


def register_cors(app: FastAPI) -> None:
    """
    支持跨域
    :param app:
    :return:
    """
    if settings.DEBUG:
        app.add_middleware(
            CORSMiddleware,
            allow_origins=["*"],
            allow_credentials=True,
            allow_methods=["*"],
            allow_headers=["*"],
        )


def register_exception(app: FastAPI) -> None:
    """
    全局异常捕获
    注意 别手误多敲一个s
    exception_handler
    exception_handlers
    两者有区别
        如果只捕获一个异常 启动会报错
        @exception_handlers(UserNotFound)
    TypeError: 'dict' object is not callable
    :param app:
    :return:
    """

    @app.exception_handler(ValidationException)
    async def inner_validation_exception_handler(request: Request, exc: ValidationException):
        """
        内部参数验证异常
        :param request:
        :param exc:
        :return:
        """
        logger.error(
            f"内部参数验证错误\nURL:{request.method}{request.url}\nHeaders:{request.headers}\n{traceback.format_exc()}")
        return response_code.resp_500(data=exc.errors(), message="参数异常")

    @app.exception_handler(custom_exc.AuthenticationError)
    async def user_not_found_exception_handler(request: Request, exc: custom_exc.AuthenticationError):
        """
        用户权限不足
        :param request:
        :param exc:
        :return:
        """
        logger.error(f"用户权限不足 \nURL:{request.method}{request.url}")
        return response_code.resp_500(data=exc.detail, message="权限不足")

    @app.exception_handler(custom_exc.DataBasedError)
    async def database_exception_handler(request: Request, exc: custom_exc.DataBasedError):
        """
        数据库处理处理异常
        :param request:
        :param exc:
        :return:
        """
        logger.error(f"数据库处理异常 \nURL:{request.method}{request.url}")
        return response_code.resp_500(data=exc.detail, message="数据库处理处理异常")

    @app.exception_handler(custom_exc.GeneralError)
    async def general_exception_handler(request: Request, exc: custom_exc.GeneralError):
        """
        一般异常
        :param request:
        :param exc:
        :return:
        """
        logger.error(f"一般异常 \nURL:{request.method}{request.url}")
        return response_code.resp_500(data=exc.detail, message="一般异常")

    @app.exception_handler(RequestValidationError)
    async def request_validation_exception_handler(request: Request, exc: RequestValidationError):
        """
        请求参数验证异常
        :param request:
        :param exc:
        :return:
        """
        logger.error(
            f"请求参数格式错误\nURL:{request.method}{request.url}\nHeaders:{request.headers}\n{traceback.format_exc()}")
        # return response_code.resp_4001(message='; '.join([f"{e['loc'][1]}: {e['msg']}" for e in exc.errors()]))
        return response_code.resp_500(data=exc.errors(), message="参数验证异常")

    # 捕获全部异常
    @app.exception_handler(Exception)
    async def all_exception_handler(request: Request, exc: Exception):
        """
        全局所有异常
        :param request:
        :param exc:
        :return:
        """
        logger.error(
            f"全局异常\n{request.method}URL:{request.url}\nHeaders:{request.headers}\n{traceback.format_exc()}")
        return response_code.resp_500(data=None, message="未知错误")


def register_hook(app: FastAPI) -> None:
    """
    请求响应拦截 hook
    :param app:
    :return:
    """

    @app.middleware("http")
    async def logger_request(request: Request, call_next) -> Response:
        logger.info(
            f"访问记录:{request.method} url:{request.url}\nheaders:{request.headers}\nIP:{request.client.host}")
        response = await call_next(request)
        return response

    


def register_router(app: FastAPI) -> None:
    """
    注册路由
    :param app:
    :return:
    """
    # 项目API
    app.include_router(
        api_v1_router,
    )


def register_init(app: FastAPI) -> None:
    """
    初始化连接
    :param app:
    :return:
    """

    @app.on_event("startup")
    async def init_connect():
        pass
        # 连接redis
        # redis_client.init_redis_connect()
       # SingletonAiohttp.get_aiohttp_client()

    @app.on_event('shutdown')
    async def shutdown_connect():
        """
        关闭
        :return:
        """
        pass
       # await SingletonAiohttp.close_aiohttp_client()
